<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Frame;

use Srv\Libs\Common\CommJson;
use Srv\Libs\Common\CommString;
use Srv\Libs\Common\CommTime;
use Srv\Libs\Storage\DataRedis;

class CacheList
{
    public const NEXT_KEY_MIN                       = 8;        // 分页KEY最小数
    public const NEXT_KEY_MAX                       = 12;       // 分页KEY最大数
    public const PAGE_SIZE_MIN                      = 1;        // 分页最小数
    public const PAGE_SIZE_MAX                      = 1000;     // 分页最大数
    public const EXPIRE_TIME_MIN                    = 60;       // 缓存有效期最小数
    public const EXPIRE_TIME_MAX                    = 86400;    // 缓存有效期最大数
    public const KEY_LIST                           = 'list';   // KEY-列表数据
    public const KEY_TIME                           = 'time';   // KEY-创建时间
    private string $listPrefix                      = '';       // 缓存前缀
    private int $listExpire                         = 0;        // 缓存有效时长[秒]
    private int $pageSize                           = 0;        // 分页大小
    private array $listData                         = [];       // 列表数据
    private bool $listDataCreate                    = false;    // 列表创建数据
    private int $totalSize                          = 0;        // 列表总数
    private string $nextKey                         = '';       // 分页KEY
    private int $nextPos                            = 0;        // 分页位置
    private int $createTime                         = 0;        // 创建时间[秒]
    private ?DataRedis $CcAuto                      = null;     // CcAuto
    private array $listItem                         = [];       // 列表项
    private int $posItem                            = 0;        // 列表位置

    /**
     * @param string $listPrefix
     * @param int $listExpire
     * @param int $pageSize
     * @param string $next
     * @param int $errType
     * @return static
     * fromCache
     */
    public static function fromCache(string $listPrefix, int $listExpire, int $pageSize, string $next, int &$errType):self
    {
        $__class__              = static::class;
        $that                   = new $__class__();
        $that->listDataCreate   = false;
        $that->listPrefix       = $listPrefix;
        $that->listExpire       = min(max($listExpire, self::EXPIRE_TIME_MIN), self::EXPIRE_TIME_MAX);
        $that->pageSize         = min(max($pageSize, self::PAGE_SIZE_MIN), self::PAGE_SIZE_MAX);
        $nextKey                = '';
        $nextPos                = 0;
        if(!$that->parserNext($next, $nextKey, $nextPos) || strlen($nextKey) < 1){
            $errType            = 1;
            return $that;
        }
        $that->nextKey          = $nextKey;
        $that->nextPos          = $nextPos;
        $that->CcAuto           = ControllerAbstract::getCacheAuto();
        $cacheListData          = $that->CcAuto->getAry($that->listPrefix.$that->nextKey);
        if(!isset($cacheListData[self::KEY_LIST]) || !isset($cacheListData[self::KEY_TIME]) || !is_array($cacheListData[self::KEY_LIST]) || !is_int($cacheListData[self::KEY_TIME])){
            $errType            = 2;
            return $that;
        }
        $that->listData         = $cacheListData[self::KEY_LIST];
        $that->totalSize        = count($that->listData);
        $that->createTime       = $cacheListData[self::KEY_TIME];
        return $that;
    }

    /**
     * @param string $listPrefix
     * @param int $listExpire
     * @param int $pageSize
     * @param array $listData
     * @return static
     * create
     */
    public static function create(string $listPrefix, int $listExpire, int $pageSize, array $listData):self
    {
        $__class__              = static::class;
        $that                   = new $__class__();
        $that->listDataCreate   = true;
        $that->listPrefix       = $listPrefix;
        $that->listExpire       = min(max($listExpire, self::EXPIRE_TIME_MIN), self::EXPIRE_TIME_MAX);
        $that->pageSize         = min(max($pageSize, self::PAGE_SIZE_MIN), self::PAGE_SIZE_MAX);
        $that->listData         = $listData;
        $that->totalSize        = count($listData);
        $that->createTime       = CommTime::getTimeStamp();
        $that->nextKey          = CommString::genRandStr(mt_rand(self::NEXT_KEY_MIN, self::NEXT_KEY_MAX));
        $that->nextPos          = 0;
        $that->CcAuto           = ControllerAbstract::getCacheAuto();
        return $that;
    }

    /**
     * @return int
     * getTotalSize
     */
    public function getTotalSize():int
    {
        return $this->totalSize;
    }

    /**
     * @return int
     * getCreateTime
     */
    public function getCreateTime():int
    {
        return $this->createTime;
    }

    /**
     * @return bool
     * isEmptyPosList
     */
    public function isEmptyPosList():bool
    {
        return ($this->totalSize < 1 || $this->nextPos > ($this->totalSize-1));
    }

    /**
     * @return array
     * getDataList
     */
    public function getDataList():array
    {
        return $this->listData;
    }

    /**
     * @return int
     * getNextPos
     */
    public function getNextPos():int
    {
        return $this->nextPos;
    }

    /**
     * @return int
     * getPageSize
     */
    public function getPageSize():int
    {
        return $this->pageSize;
    }

    /**
     * @param int $pos
     * @return bool
     * isNextPos
     */
    public function isNextPos(int $pos):bool
    {
        return $pos >= $this->nextPos;
    }

    /**
     * @param VOAbstract $listItemVO
     * @return bool
     * addItemIsNext
     */
    public function addItemIsNext(VOAbstract $listItemVO):bool
    {
        $this->listItem[] = $listItemVO;
        return count($this->listItem) < $this->pageSize;
    }

    /**
     * @param bool $isSave
     * @return array
     * getItemList
     */
    public function getItemList(bool $isSave = false):array
    {
        if($isSave) $this->saveCache();
        return $this->listItem;
    }

    /**
     * @return bool
     * isEndPos
     */
    public function isEndPos():bool
    {
        return $this->posItem >= ($this->totalSize-1);
    }

    /**
     * @return void
     * saveCache
     */
    private function saveCache():void
    {
        if($this->listDataCreate){
            $this->CcAuto->set($this->listPrefix.$this->nextKey, CommJson::encodeArray([self::KEY_LIST => $this->listData, self::KEY_TIME => CommTime::getTimeStamp()]), $this->listExpire);
        }else{
            $this->CcAuto->setExpire($this->listPrefix.$this->nextKey, $this->listExpire);
        }
    }

    /**
     * @param int $pos
     * @return string
     * genNext
     */
    public function genNext(int $pos):string
    {
        $this->posItem      = $pos;
        if($this->posItem < 0 || $this->isEndPos()) return '';
        $next               = $this->nextKey.'-'.(++$this->posItem);
        return preg_match('/^[a-zA-Z\d]+-\d+$/i', $next) ? $next : '';
    }

    /**
     * @param string $next
     * @param string $nextKey
     * @param int $nextPos
     * @return bool
     * parserNext
     */
    private function parserNext(string $next, string &$nextKey, int &$nextPos):bool
    {
        if(strlen($next) < 3) return false;
        if(preg_match('/^([a-zA-Z\d]+)-(\d+)$/i', $next, $match)){
            $nextKey        = $match[1];
            $nextPos        = intval($match[2]);
            return true;
        }
        return false;
    }
}